#include "httpconn.h"
#include <memory>

using namespace std;

/*因为静态成员变量是所有类共享的*/
/*所以需要单独拿出来初始化*/
/*准确来说 静态成员变量是属于类的 而不是属于类初始化的对象的*/
const char* HttpConn::srcDir;
std::atomic<int> HttpConn::userCount;

// 默认初始化值为false 表示水平触发 也就是一次就读一下
bool HttpConn::isET = true;
/*静态成员函数没有 this 指针，只能访问静态成员（包括静态成员变量和静态成员函数)*/


static EventLoop *CheckLoopNotNull(EventLoop *loop)
{
    if (loop == nullptr)
    {
        LOG_ERROR(" mainLoop is null!");
    }
    return loop;
}

HttpConn::HttpConn(
        EventLoop *loop,                  
        int sockfd,
        const std::string &nameArg,
        InetAddress &localAddr,
        const InetAddress &peerAddr)    
    : loop_(CheckLoopNotNull(loop))
    , socket_(new Socket(sockfd))
    , channel_(new Channel(loop, sockfd))   // channel绑定了loop和fd
    , name_(nameArg)
    , localAddr_(localAddr)
    , peerAddr_(peerAddr)
{
    // 下面给channel设置相应的回调函数 poller给channel通知感兴趣的事件发生了 channel会回调相应的回调函数
    channel_->setReadCallback(
        std::bind(&HttpConn::handleRead, this,std::placeholders::_1));
    channel_->setWriteCallback(
        std::bind(&HttpConn::handleWrite, this));
    channel_->setCloseCallback(
        std::bind(&HttpConn::handleClose, this));
    channel_->setErrorCallback(
        std::bind(&HttpConn::handleError, this));

    //LOG_INFO("TcpConnection:ctor:at fd=[%d]",sockfd);
    socket_->setKeepAlive(true);

    userCount++;
    writeBuff_.RetrieveAll();
    readBuff_.RetrieveAll();
    isClose_ = false;
}

HttpConn::~HttpConn() { 
    Close(); 
};



void HttpConn::Close() {
    response_.UnmapFile();
    if(isClose_ == false) {
        isClose_ = true;
        userCount--;
        LOG_INFO("Client[%d](%s:%d) quit, UserCount:%d", socket_->fd(), GetIP(), GetPort(), (int)userCount);
    }
}

int HttpConn::GetFd() const {
    return socket_->fd();
};

struct sockaddr_in HttpConn::GetAddr() const {
    return *localAddr_.getSockAddr();
}

const char* HttpConn::GetIP() const {
    return inet_ntoa(localAddr_.getSockAddr()->sin_addr);
}

int HttpConn::GetPort() const {
    return localAddr_.getSockAddr()->sin_port;
}
/*包括两部分 read和Process */
/*问题是如果是水平触发 得等读完了才能操作*/
/*Process做判断了*/

ssize_t HttpConn::read(int* saveErrno) {
    ssize_t len = -1;
    do {
        // 把数据读出来 第一次读就是0 是不应该的
        len = readBuff_.ReadFd(channel_->fd(), saveErrno);
        if (len <= 0) {
            break;
        }

    }while(isET);
    return len;
}


ssize_t HttpConn::write(int* saveErrno) {
    ssize_t len = -1;
    do {
        len = writev(socket_->fd(), iov_, iovCnt_);
        if(len <= 0) {
            *saveErrno = errno;
            break;
        }
        if(iov_[0].iov_len + iov_[1].iov_len  == 0) { break; } /* 传输结束 */
        else if(static_cast<size_t>(len) > iov_[0].iov_len) {
            // 说明第一块已经被写入了
            iov_[1].iov_base = (uint8_t*) iov_[1].iov_base + (len - iov_[0].iov_len);
            iov_[1].iov_len -= (len - iov_[0].iov_len);
            if(iov_[0].iov_len) {
                /*为什么这里要清空呢*/
                writeBuff_.RetrieveAll();
                iov_[0].iov_len = 0;
            }
        } else {/*第一块还没写完呢*/
            iov_[0].iov_base = (uint8_t*)iov_[0].iov_base + len; 
            iov_[0].iov_len -= len; 
            writeBuff_.Retrieve(len);
        }
    } while(isET || ToWriteBytes() > 10240);
    return len;
}

void HttpConn::OnProcess() {
    if(process()) {/*处理成功了就是可写了*/
       channel_->setWriting();
    } else {/*要不然还保持原样*/
        channel_->setReading();
    }
}
// 如果真有ERROR 那么主线程应该会通过错误回调函数关闭这个fd
void HttpConn::handleRead(Timestamp stamp) {
    //LOG_INFO("handle Read");
    ssize_t len = -1;
    int readErrno = 0;
    len = read(&readErrno);         /*如果一次性没有读完呢?*/
    /*把数据放入到readBuff中了*/
    if(len <= 0 && readErrno != EAGAIN) {
        //LOG_ERROR("client fd : [%d] read Error len :[%d] errno : [%d]",channel_->fd(),len,readErrno);
        handleClose();
        return;
    }
    //LOG_INFO("client fd : [%d] read Success len :[%d]",channel_->fd(),len);
    OnProcess();
}

// 把iov的数据写到fd_中
/*这里和writeBuff_的联动有待商榷*/
void HttpConn::handleWrite() {
    //LOG_INFO("handle Write");
    ssize_t len = -1;
    int writeErrno = 0;
    len = write(&writeErrno);
    if(ToWriteBytes() == 0) {/*表示写完了 水平模式下可能只写一次写不完*/
        /* 传输完成 */
        if(IsKeepAlive()) {
            OnProcess(); /*改为只是可读了*/
            return;
        }
    }    
    /*可能是数据量太大了 就等epoller通知下次可写*/
    else if(len < 0) {
        if(writeErrno == EAGAIN) {
            channel_->setWriting();
            /* 继续传输保持可写就行 */
            return;
        }
    }
   //LOG_ERROR("client fd : [%d] write Error len :[%d] errno : [%d]",channel_->fd(),len,writeErrno);
    handleClose();
}

void HttpConn::handleClose()
{
    //LOG_INFO("TcpConnection fd=[%d] close",channel_->fd());
    channel_->disableAll();
    channel_->remove();
    Close();
    HttpConnPtr connPtr(shared_from_this());
    if(connectionCallback_)
        connectionCallback_(connPtr); // 连接回调
    if(closeCallback_)
        closeCallback_(connPtr);      // 执行关闭连接的回调 执行的是TcpServer::removeConnection回调方法   // must be the last line
}
// 在这里 发生Error只是记录了一下吗
// 还是说主线程会关闭的呢

void HttpConn::handleError()
{
    int optval;
    socklen_t optlen = sizeof optval;
    int err = 0;
    if (::getsockopt(channel_->fd(), SOL_SOCKET, SO_ERROR, &optval, &optlen) < 0)
    {
        err = errno;
    }
    else
    {
        err = optval;
    }
    //LOG_ERROR("TcpConnection::handleError name:[%s]- SO_ERROR:%d",name_.c_str(),err);
}

/*要绑定谁呢*/
bool HttpConn::process() {
    request_.Init();
    if(readBuff_.ReadableBytes() <= 0) {
        return false;
    }
    else if(request_.parse(readBuff_)) {
        LOG_DEBUG("%s", request_.path().c_str());
        response_.Init(srcDir, request_.path(), request_.IsKeepAlive(), 200);
    } else {
        response_.Init(srcDir, request_.path(), false, 400);
    }
    /*把响应头写到buffer里面*/
    /*避免频繁的malloc系统调用*/
    response_.MakeResponse(writeBuff_);

    /* 响应头 */
    iov_[0].iov_base = const_cast<char*>(writeBuff_.Peek());
    iov_[0].iov_len = writeBuff_.ReadableBytes();
    iovCnt_ = 1;

    /* 文件 */
    if(response_.FileLen() > 0  && response_.File()) {
        iov_[1].iov_base = response_.File();
        iov_[1].iov_len = response_.FileLen();
        iovCnt_ = 2;
    }
    //LOG_DEBUG("filesize:%d, %d  to %d", response_.FileLen() , iovCnt_, ToWriteBytes());
    return true;
}
/*这两个都是由主线程调用的 不是httpconn所属的线程*/
// 连接建立
void HttpConn::connectEstablished()
{
    channel_->tie(shared_from_this());
    //LOG_INFO("channel_fd:[%d] enable Reading",channel_->fd());
    channel_->enableReading(); // 向poller注册channel的EPOLLIN读事件(此时就该调用updte了)
    // 新连接建立 执行回调
    if(connectionCallback_)
        connectionCallback_(shared_from_this());
}
// 连接销毁
void HttpConn::connectDestroyed()
{
    channel_->disableAll(); // 把channel的所有感兴趣的事件从poller中删除掉
    if(connectionCallback_)
        connectionCallback_(shared_from_this());    /*这个回调就没执行过 空的*/
    channel_->remove(); // 把channel从poller中删除掉
    HttpConn::Close();
}